<?php
namespace app\lib;
use Firebase\JWT\JWT;
use think\facade\Cache;
use think\facade\Env;

/**
 * Token
 * Class Token
 * @package app\lib
 */
class Token{
    /**
     * 密钥
     */
    private $key;

    /**
     * Token constructor.
     */
    public function __construct()
    {
        $this->key=Env::get("jwt.key")?Env::get("jwt.key"):"LXYYJWTkKey";
    }

    /**
     * 生成JWT
     * @param $uid 用户ID
     * @param int $exp JWT过期时间 默认设置为2小时
     * @param bool $redis 是否存入redis 用作动态刷新时间,默认为false
     * @param int $redis_exp redis 数据过期时间，默认设置为4小时
     * @return string
     */
    public  function signToken($uid,$redis=false,$redis_exp=14400){
        $token=array(
            "iss"=>"lxyy.com",//签发者,可为空
            "sub"=>"", // JWT所面向的用户
            "aud"=>"",//接收JWT的一方
            "iat"=>time(),//签发时间
            "nbf"=>time(),//not before 在这个时间之前，token是无效的
            "exp"=>time()+604800,//过期时间,设置7天
            'data'=>[
                "uid"=>$uid //自定义信息，这里是存储用户的ID
            ]
        );
        $jwt=JWT::encode($token,$this->key,"HS256");
        if($redis){
            //使用Redis缓存
            Cache::store("redis")->set("uid_".$uid,"{$jwt}",$redis_exp);
        }
        return $jwt;//生成token
    }

    /**
     * 续期token，续期成功返回新的token，否则返回false
     * @param $token
     * @return bool|mixed
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public  function refreshToken($token){
        $key=$this->getUid($token);
        if (!$key){
            return  false;
        }
            $isset=Cache::store("redis")->get("uid_".$key);
            if ($isset){
                //避免多个请求携带过期请求，导致多次刷新token
                //判断当前token是否与Redis中的token相同，如果不相同，返回Redis中的新token，如过相同，生成新token并且替换Redis中的token
                if($isset==$token){
                    $newToken=$this->signToken($key);
                    Cache::store("redis")->set("uid_".$key,$newToken,0);
                    return  $newToken;
                }else{
                    return $isset;
                }
            }
        return  false;
    }
    /**
     * 获取UID
     * @param $token
     * @return bool|mixed
     */
    public function  getUid($token){
        try {
            $tks = \explode('.', $token);
            list($headb64, $bodyb64, $cryptob64) = $tks;
            $decode=json_decode(base64_decode($bodyb64),JSON_UNESCAPED_UNICODE);
            $uid=$decode["data"]["uid"];
            return $uid;
        }catch (\Exception $e){
            return  false;
        }
    }
    /**
     * 检查token是否过期
     * @param $token
     * @return int[]
     */
    public function checkToken($token){
        try {
            JWT::$leeway = 0;//当前时间减去0，把时间留点余地
            $decoded = JWT::decode($token, $this->key, array('HS256')); //HS256方式，这里要和签发的时候对应
            $arr = (array)$decoded;
            $res['code']=1000;
            $res['data']=$arr['data'];
            return $res;
        } catch(\Firebase\JWT\SignatureInvalidException $e) { //签名不正确
            $status=array("code"=>1001);
            $status['msg']="签名不正确";
            return $status;
        }catch(\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用
            $status=array("code"=>1002);
            $status['msg']="token失效";
            return $status;
        }catch(\Firebase\JWT\ExpiredException $e) { // token过期
            $status=array("code"=>1003);
            $status['msg']="token过期";
            return $status;
        }catch(\Exception $e) { //其他错误
            $status=array("code"=>1004);
            $status['msg']="未知错误";
            return $status;
        }
    }
}